home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1997 September
/
Macworld (1997-09).dmg
/
Serious Software
/
Cherwell Scientific Demos
/
pro Fit
/
pro Fit 5.0 demo (fpu).sea
/
pro Fit 5.0 demo (fpu)
/
External Modules
/
External modules sources
/
Pascal
/
FFT.p
next >
Wrap
Text File
|
1994-09-28
|
11KB
|
396 lines
{ This unit contains the function 'fft' which, together with 350 additional }
{ programs useful in scientific work, is found in the book 'Numerical Recipes: }
{ The Art of Scientific Computing', available from Cambridge University Press. }
{ It is used here by permission of the authors. }
{************************************************************************************}
{ FFT.p }
{ }
{ }
{ Version 26.9.94 }
{************************************************************************************}
unit user;
interface
uses
proFit_interface;
procedure main (selector: integer; pb: ExtModulesParamBlockPtr);
implementation
{ note: MPW users must make sure that the procedure main is at the beginning of the compiled code }
{ under Think Pascal, this is cared for by the compiler }
{ We let main call a function mainMain to make sure that the code starts with a jump to }
{ our entry point even when compiling under MPW Pascal }
procedure mainMain (selector: integer; pb: ExtModulesParamBlockPtr);
forward;
procedure main (selector: integer; pb: ExtModulesParamBlockPtr);
begin
mainMain(selector, pb);
end;
{--------------------------------------------------------------------------------------------------------}
type
data = array[1..8192] of extended;
dataptr = ^data;
procedure WriteStringAndNumber (s: str255; num: longint);
begin
WriteString(s);
NumToString(num, s);
WritelnString(s);
end; { WriteStringAndNumber }
procedure fft (x: dataptr; num: integer; inverse: boolean);
var
ii, jj, n, mmax, m, j, istep, i: integer;
wtemp, wr, wpr, wpi, wi, theta, tempr, tempi: extended;
begin
n := num;
j := 1;
for ii := 1 to (n div 2) do
begin
i := 2 * ii - 1;
if j > i then
begin
tempr := x^[j];
tempi := x^[j + 1];
x^[j] := x^[i];
x^[j + 1] := x^[i + 1];
x^[i] := tempr;
x^[i + 1] := tempi;
end;
m := n div 2;
while ((m >= 2) & (j > m)) do
begin
j := j - m;
m := m div 2;
end;
j := j + m;
if TestStop then { test if proFit or a user tries to stop the program }
exit(fft);
end; { for ii := 1 to (n div 2) do }
mmax := 2;
while n > mmax do
begin
istep := 2 * mmax;
theta := 6.283185307959 / mmax;
if inverse then
theta := -theta;
wpr := -2 * sqr(sin(0.5 * theta));
wpi := sin(theta);
wr := 1;
wi := 0;
for ii := 1 to (mmax div 2) do
begin
m := 2 * ii - 1;
for jj := 0 to ((n - m) div istep) do
begin
i := m + jj * istep;
j := i + mmax;
tempr := wr * x^[j] - wi * x^[j + 1];
tempi := wr * x^[j + 1] + wi * x^[j];
x^[j] := x^[i] - tempr;
x^[j + 1] := x^[i + 1] - tempi;
x^[i] := x^[i] + tempr;
x^[i + 1] := x^[i + 1] + tempi;
end; { for jj := 0 to ((n - m) div istep) do }
wtemp := wr;
wr := wr + wr * wpr - wi * wpi;
wi := wi + wi * wpr + wtemp * wpi;
if TestStop then { test if proFit or a user tries to stop the program }
exit(fft);
end; { for ii := 1 to (mmax div 2) do }
mmax := istep;
end; { while n>mmax }
end; { fft }
{-----------------------------------------}
procedure shiftFFT (x: dataptr; np: integer);
var
xx: extended;
i, np2: integer;
begin
for i := 1 to np do
begin
xx := x^[i];
x^[i] := x^[i + np];
x^[i + np] := xx;
end;
end; { shiftFFT }
{************************************************************************************}
procedure SetUp (var moduleKind: integer; { set moduleKind to isFunction or isProgram }
var name: Str255; { the name of the program or function }
var requiredGlobals: longint; { the number of bytes to be allocated in ExtModulesParamBlock.globals }
{ set requiredGlobals to 0 if you don't use this feature }
pb: ExtModulesParamBlockPtr); { the complete parameter block passed by pro Fit to the }
{ routines defined in this file. In most cases it can be ignored }
{ SetUp is called once when the external module is linked to pro Fit }
begin
moduleKind := isProgram;
name := 'Complex FFT';
requiredGlobals := 0;
end;
{************************************************************************************}
procedure InitializeProg (pb: ExtModulesParamBlockPtr);
{ Can be left emtpy if not needed. }
{ called when the external module is linked to proFit after SetUp was called }
{ can be used to inititialize global variables, etc. }
begin
pb^.V[1] := 0; {used as a flag to find out when the FFT program is called for the first time}
pb^.V[2] := 64;{ the default input parameters}
pb^.V[3] := 1;
pb^.V[4] := 4;
pb^.V[5] := 0;
pb^.V[6] := 1;
end;
{************************************************************************************}
procedure Run (pb: ExtModulesParamBlockPtr);
{ pro Fit calls this function when the name of the program is chosen from the }
{ Run Program submenu in the menu Calc }
var
s1, s2, s3, s4, s5, st: str255;
r: inputRec;
ex1, ex2, ex3, ex4, ex5, x0, xinc, y0, yinc, yoff: extended;
i, xCol, yCol: integer;
x: dataptr; { pointer to the data array }
nrData: integer; { the number of data (integer power of 2) }
inverse: boolean; { false if normal FFT should be done, true for reverse FFT }
answer: integer;
procedure CleanUpRun;
begin
StopExecution;
if x <> nil then
DisposPtr(pointer(x)); { we don't need the memory any more }
exit(Run); { terminate this call of run }
end;
begin
if pb^.V[1] = 0 then
begin { for the first time this program runs }
pb^.V[1] := 1;
if AskBox(answer, 'Do you want to print some information about this program to the Results window ?') then
exit(Run) { stop button pressed }
else if answer = 1 then { yes button pressed }
begin
WritelnString('The number of data points must be between 4 and 4096.');
WritelnString('(The program sets the number of data points to the next smaller power of 2)');
WritelnString('"First column No of data" means the column number where the "time" values are stored.');
WritelnString('The real and imaginary values of your data must be in the succeeding two columns.');
WritelnString('"First column No of FFT" means the column number where the "frequency" values will be stored.');
WritelnString('The real and imaginary part of the Fourier transformed data will be in the succeeding two columns.');
WritelnString('"Offset of FFT" defines an additive constant for the frequency values.');
exit(Run);
end;
end;
ex1 := pb^.v[2]; { do dialog about dataNr, ColumnNrs }
s1 := 'Number of data points';
r[1].x := pointer(@ex1);
r[1].s := pointer(@s1);
ex2 := pb^.v[3];
s2 := 'First column No. of data';
r[2].x := pointer(@ex2);
r[2].s := pointer(@s2);
ex3 := pb^.v[4];
s3 := 'First column No of FFT';
r[3].x := pointer(@ex3);
r[3].s := pointer(@s3);
ex4 := pb^.v[5];
s4 := ' Offset of FFT';
r[4].x := pointer(@ex4);
r[4].s := pointer(@s4);
ex5 := pb^.v[6];
s5 := 'FFT (>0) ; inverse FFT (<0) ?';
r[5].x := pointer(@ex5);
r[5].s := pointer(@s5);
if InputBox(5, r) then
exit(Run) { if something is not ok we quit }
else
begin { if everything is ok we do something }
ex1 := abs(ex1);
if (ex1 > 4096) | (ex1 < 4) then
exit(Run);
nrData := round(exp(ln(2) * trunc(ln(ex1) / ln(2))));
ex2 := abs(ex2);
if (ex2 > 97) | (ex2 < 1) then
exit(Run);
xCol := round(ex2);
ex3 := abs(ex3);
if (ex3 > 97) | (ex3 < 1) then
exit(Run);
yCol := round(ex3);
yoff := ex4;
if ex5 >= 0 then
inverse := false
else
inverse := true;
pb^.v[2] := nrData;{set the default parameter to the chosen values, for the next time FFT is called}
pb^.v[3] := ex2;
pb^.v[4] := ex3;
pb^.v[5] := ex4;
if inverse then
pb^.v[6] := -1
else
pb^.v[6] := 1;
x := dataptr(newPtr(sizeof(extended) * longint(2) * nrData)); { prepare memory for data }
if x = nil then
exit(run);
if TestData(1, xCol) then
begin
x0 := GetData(1, xCol);
if TestData(2, xCol) then
xinc := GetData(2, xCol) - x0;
end;
for i := 1 to NrData do { read data from window }
begin
if TestData(i, xCol + 1) then { test if data value is ok }
x^[2 * i - 1] := GetData(i, xCol + 1)
else
x^[2 * i - 1] := 0;
if TestData(i, xCol + 2) then
x^[2 * i] := GetData(i, xCol + 2)
else
x^[2 * i] := 0;
if TestStop then
CleanUpRun;
end;
fft(x, 2 * NrData, inverse); { fft calculations }
yinc := 1 / NrData / xinc;
if not inverse then
begin
shiftFFT(x, NrData);
y0 := yoff - 1 / xinc / 2;
end
else
begin
y0 := yoff;
for i := 1 to NrData div 2 do
begin
x^[4 * i - 3] := x^[4 * i - 3] / NrData;
x^[4 * i - 2] := x^[4 * i - 2] / NrData;
x^[4 * i - 1] := -x^[4 * i - 1] / NrData;
x^[4 * i] := -x^[4 * i] / NrData;
end;
end;
if TestStop then
CleanUpRun;
SetColName(yCol, 'frequency');
SetColName(yCol + 1, 'FFT real');
SetColName(yCol + 2, 'FFT imag.');
for i := 1 to NrData do { output of data into window }
begin
SetData(i, yCol, y0 + (i - 1) * yinc);
SetData(i, yCol + 1, x^[2 * i - 1]);
SetData(i, yCol + 2, x^[2 * i]);
if TestStop then
CleanUpRun;
end;
if x <> nil then
DisposPtr(Ptr(x));{ we don't need the memory any more }
end;
end;{Run}
{************************************************************************************}
procedure CleanUp (pb: ExtModulesParamBlockPtr);
{ called when the function or program is removed from pro Fit's menus }
{ in most cases, this function can be empty }
begin
end;
{***********************************************************************************************}
{ This is the main procedure through which all calls to the external module go. }
{ Main takes care of calling the right procedure with the right parameters depending on }
{ the value of "selector". }
{ You don't need to touch this procedure }
procedure mainMain (selector: integer; pb: ExtModulesParamBlockPtr);
begin
Startup(pb);
case selector of
kSetup:
begin
pb^.requiredGlobals := 0;
pb^.versionNumber := VERSIONNUMBER;
if sizeof(extended) = 10 then
pb^.codeType := CPU68noFPU
else if sizeof(extended) = 12 then
pb^.codeType := CPU68FPU
else
pb^.codeType := CPUPowerPC;
SetUp(pb^.moduleKind, pb^.name, pb^.requiredGlobals, pb);
end;
progInitialize:
InitializeProg(pb);
progRun:
Run(pb);
kCleanUp:
CleanUp(pb);
otherwise
end;
end;
end.